import { Dimension } from '../../common/dimension';
import { IDrawParams } from '../../rendering/draw-params.interface';
import { RenderingContext } from '../../rendering/rendering-context';
import { IRenderingContextConfig } from '../../rendering/rendering-context-config.interface';
import { MathUtils } from '../../utilities/math-utils';
import { IWorld } from '../common/world.interface';
import { BorderRendererComponent } from '../components/border-renderer.component';
import { CameraComponent } from '../components/camera.component';
import { ImageRendererComponent } from '../components/image-renderer.component';
import { LocationComponent } from '../components/location.component';
import { RectangleRendererComponent } from '../components/rectangle-renderer.component';
import { RendererComponent } from '../components/renderer.component';
import { SpriteAnimationRendererComponent } from '../components/sprite-animation-renderer.component';
import { SpriteRendererComponent } from '../components/sprite-renderer.component';
import { System } from './system';

export const DEF_STROKE_STYLE = 'black';

export interface IRenderConfig {
  shouldAdjustResolution: boolean;
  resolutionAdjustmentDelay: number;
}

export class RenderSystem extends System {
  #elapsed: number = 0;
  readonly #context: RenderingContext;

  constructor(
    private readonly canvasElement: HTMLCanvasElement,
    private readonly config: IRenderConfig,
    private readonly contextConfig: IRenderingContextConfig
  ) {
    super();

    this.#context = new RenderingContext(canvasElement, contextConfig);
  }

  onStart(world: IWorld) {
    const cameras = world.findEntitiesByComponents([LocationComponent, CameraComponent]);
    if (cameras.length === 0) {
      return;
    }

    const cameraComponent = world.findComponentByEntity(cameras[0], CameraComponent)!;
    this.#context.changeDimensionInUnits(cameraComponent.viewDimension);
  }

  onUpdate(world: IWorld, dt: number) {
    this.adjustResolution(dt);

    this.#context.clear();

    const cameras = world.findEntitiesByComponents([LocationComponent, CameraComponent]);
    if (cameras.length === 0) {
      return;
    }

    const cameraLocationComponent = world.findComponentByEntity(cameras[0], LocationComponent)!;
    const cameraComponent = world.findComponentByEntity(cameras[0], CameraComponent)!;

    const viewLocation = cameraLocationComponent.location;
    const viewDimension = cameraComponent.viewDimension;

    const entities = world.findEntitiesByComponents([LocationComponent, RendererComponent]);
    for (const entity of entities) {
      const locationComponent = world.findComponentByEntity(entity, LocationComponent)!;
      const rendererComponent = world.findComponentByEntity(entity, RendererComponent)!;

      const { location } = locationComponent;
      const { dimension } = rendererComponent;

      if (MathUtils.isIntersection(location, dimension, viewLocation, viewDimension)) {
        const locationOnView = location.substract(viewLocation);

        if (rendererComponent instanceof RectangleRendererComponent) {
          this.#context.drawRectangle(locationOnView, dimension, rendererComponent.fillStyle);
        } else if (rendererComponent instanceof BorderRendererComponent) {
          this.#context.drawBorder(locationOnView, dimension, rendererComponent.strokeStyle);
        } else if (rendererComponent instanceof ImageRendererComponent) {
          this.#context.drawImage(rendererComponent.image, locationOnView, dimension);
        } else if (rendererComponent instanceof SpriteRendererComponent) {
          const params: IDrawParams = {
            image: rendererComponent.sprite,
            sourceLocationInPixels: rendererComponent.sourceLocation,
            sourceDimensionInPixels: rendererComponent.sourceDimension,
            targetLocationInUnits: locationOnView,
            targetDimensionInUnits: dimension,
          };
          this.#context.drawSprite(params);
        } else if (rendererComponent instanceof SpriteAnimationRendererComponent) {
          if (!rendererComponent.isComplete) {
            const params: IDrawParams = {
              image: rendererComponent.sprite,
              sourceLocationInPixels: rendererComponent.currentFrame.sourceLocation,
              sourceDimensionInPixels: rendererComponent.currentFrame.sourceDimension,
              targetLocationInUnits: locationOnView,
              targetDimensionInUnits: dimension,
            };
            this.#context.drawSprite(params);
          }
        }
      }
    }

    this.#context.drawBars();
  }

  private adjustResolution(dt: number) {
    const { shouldAdjustResolution, resolutionAdjustmentDelay } = this.config;

    if (!shouldAdjustResolution) {
      return;
    }

    const parentElement = this.canvasElement.parentElement;
    if (!parentElement) {
      return;
    }

    const resolution = new Dimension(parentElement.clientWidth, parentElement.clientHeight);
    if (this.#context.dimensionInPixels.equal(resolution)) {
      return;
    }

    if (this.#elapsed > resolutionAdjustmentDelay) {
      this.#elapsed = 0;

      this.canvasElement.width = resolution.width;
      this.canvasElement.height = resolution.height;

      this.#context.changeDimensionInPixels(resolution);
    } else {
      this.#elapsed += dt;
    }
  }
}
